/**
 * create by zhike
 * date:2015年3月2日
 */
package com.acooly.module.openapi.client.provider.sinapay;

import com.acooly.core.utils.FreeMarkers;
import com.acooly.core.utils.net.HttpResult;
import com.acooly.module.openapi.client.api.AbstractApiServiceClient;
import com.acooly.module.openapi.client.api.exception.ApiClientException;
import com.acooly.module.openapi.client.api.exception.ApiClientSocketTimeoutException;
import com.acooly.module.openapi.client.api.exception.ApiServerException;
import com.acooly.module.openapi.client.api.marshal.ApiMarshal;
import com.acooly.module.openapi.client.api.marshal.ApiUnmarshal;
import com.acooly.module.openapi.client.api.message.PostRedirect;
import com.acooly.module.openapi.client.api.transport.Transport;
import com.acooly.module.openapi.client.provider.sinapay.annotation.SinaGateWay;
import com.acooly.module.openapi.client.provider.sinapay.domain.SinapayNotify;
import com.acooly.module.openapi.client.provider.sinapay.domain.SinapayRequest;
import com.acooly.module.openapi.client.provider.sinapay.domain.SinapayResponse;
import com.acooly.module.openapi.client.provider.sinapay.exception.SinapayProcessingException;
import com.acooly.module.openapi.client.provider.sinapay.marshall.SinapayNotifyUnmarshall;
import com.acooly.module.openapi.client.provider.sinapay.marshall.SinapayRedirectPostMarshall;
import com.acooly.module.openapi.client.provider.sinapay.marshall.SinapayRequestMarshall;
import com.acooly.module.openapi.client.provider.sinapay.marshall.SinapayResponseUnmarshall;
import com.acooly.module.openapi.client.provider.sinapay.utils.HttpServletRequestUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;

/**
 * 上海银行P2P存管ApiService执行器  外部业务禁止使用
 *
 * @author zhike
 */
@Slf4j
@Component("sinapayApiServiceClient")
public class SinapayApiServiceClient
        extends AbstractApiServiceClient<SinapayRequest, SinapayResponse, SinapayNotify, SinapayNotify> {


    @Resource(name = "sinapayHttpTransport")
    private Transport transport;
    @Resource(name = "sinapayRequestMarshall")
    private SinapayRequestMarshall requestMarshal;
    @Resource(name = "sinapayResponseUnmarshall")
    private SinapayResponseUnmarshall responseUnmarshal;
    @Resource(name = "sinapayNotifyUnmarshall")
    private SinapayNotifyUnmarshall notifyUnmarshal;
    @Resource(name = "sinapayRedirectPostMarshall")
    private SinapayRedirectPostMarshall sinapayRedirectPostMarshall;

    @Autowired
    private OpenAPIClientSinapayProperties properties;

    /**
     * 同步请求
     *
     * @param request
     * @return
     */
    @Override
    public SinapayResponse execute(SinapayRequest request) {
        try {
            beforeExecute(request);
            String requestMessage = getRequestMarshal().marshal(request);
            String serviceName = request.getService();
            String url = getGatewayUrl(request);
            HttpResult result = getTransport().request(requestMessage, url);
            if (result.getStatus() >= HttpStatus.SC_BAD_REQUEST) {
                throw new RuntimeException("HttpStatus:" + result.getStatus());
            }
            SinapayResponse t = this.responseUnmarshal.unmarshal(result.getBody(), serviceName);
            afterExecute(t);
            return t;
        } catch (SinapayProcessingException pe) {
            log.error("解析响应报文异常：" + pe.getMessage(), pe);
            throw pe;
        } catch (ApiClientSocketTimeoutException ase) {
            log.error("响应超时异常：" + ase.getMessage(), ase);
            throw new SinapayProcessingException(ase.getMessage());
        } catch (ApiServerException ose) {
            log.error("服务器:" + ose.getMessage(), ose);
            throw ose;
        } catch (ApiClientException oce) {
            log.error("客户端:" + oce.getMessage(), oce);
            throw oce;
        } catch (Exception e) {
            log.error("内部错误:" + e.getMessage(), e);
            throw new ApiClientException("内部错误:" + e.getMessage());
        }
    }

    private HttpPost buildHttpPost(String url, String body, ContentType contentType) {
        HttpPost post = new HttpPost(url);
        post.setEntity(new StringEntity(body, contentType));
        return post;
    }

    private ContentType loadContentType(ContentType contentType) {
        if (contentType != null) {
            return contentType;
        }
        return ContentType.create(
                ContentType.APPLICATION_FORM_URLENCODED.getMimeType(), "utf-8");
    }


    @Override
    public String redirectGet(SinapayRequest request) {
        PostRedirect postRedirect = redirectPost(request);
        Map<String, Object> templateData = Maps.newHashMap();
        templateData.put("random", RandomStringUtils.randomAlphanumeric(10));
        templateData.put("redirectUrl", postRedirect.getRedirectUrl());
        templateData.put("formDatas", postRedirect.getFormDatas());
        String template =
                "<form accept-charset=\"utf-8\" id=\"redirectForm_${random}\" action=\"${redirectUrl}\" method=\"post\">  \n" +
                        "<#list formDatas?keys as key>\n" +
                        "    <input name=\"${key}\" value='${formDatas[key]?default(\"\")}' type='hidden'/>\n" +
                        "</#list>\n" +
                        "</form>\n" +
                        "<script>document.getElementById(\"redirectForm_${random}\").submit();</script>";
        String fromHtml = FreeMarkers.rendereString(template, templateData);
        return fromHtml;
    }

    @Override
    public PostRedirect redirectPost(SinapayRequest request) {
        try {
            // 跳转类接口，设置通知地址
            return sinapayRedirectPostMarshall.marshal(request);
        } catch (ApiServerException ose) {
            log.warn("服务器错误:" + ose.getMessage(), ose);
            throw ose;
        } catch (ApiClientException oce) {
            log.warn("客户端异常:" + oce.getMessage(), oce);
            throw oce;
        } catch (Exception e) {
            log.warn("内部错误:" + e.getMessage(), e);
            throw new ApiClientException("内部错误:" + e.getMessage());
        }
    }

    public SinapayNotify notice(HttpServletRequest request, String serviceKey) {
        try {
            Map<String, String> notifyData = HttpServletRequestUtil.getNoticeDateMap(request);
            SinapayNotify notify = getNoticeUnmarshal().unmarshal(notifyData, serviceKey);
            afterNotice(notify);
            log.info("通知报文：{}", JSON.toJSONString(notify));
            return notify;
        } catch (ApiClientException oce) {
            log.warn("客户端:{}", oce.getMessage());
            throw oce;
        } catch (Exception e) {
            log.warn("内部错误:{}", e.getMessage());
            throw new ApiClientException("内部错误:" + e.getMessage());
        }
    }

    protected String getGatewayUrl(SinapayRequest request) {
        SinaGateWay gateway = request.getClass().getAnnotation(SinaGateWay.class);
        if (gateway != null) {
            return properties.getPaymentGatewayUrl();
        }
        return properties.getMemberGatewayUrl();
    }

    @Override
    protected void beforeExecute(SinapayRequest request) {
        super.beforeExecute(request);
        request.setPartner(properties.getPartnerId());
    }

    @Override
    protected String getRedirectGateway() {
        return properties.getMemberGatewayUrl();
    }

    @Override
    protected ApiMarshal<String, SinapayRequest> getRequestMarshal() {
        return this.requestMarshal;
    }


    @Override
    protected ApiMarshal<String, SinapayRequest> getRedirectMarshal() {
        return null;
    }

    @Override
    protected ApiUnmarshal<SinapayNotify, Map<String, String>> getNoticeUnmarshal() {
        return this.notifyUnmarshal;
    }


    protected ApiUnmarshal<SinapayResponse, String> getResponseUnmarshalSpec() {
        return this.responseUnmarshal;
    }

    @Override
    protected ApiUnmarshal<SinapayResponse, String> getResponseUnmarshal() {
        return null;
    }

    @Override
    protected ApiUnmarshal<SinapayNotify, Map<String, String>> getReturnUnmarshal() {
        return this.notifyUnmarshal;
    }

    @Override
    protected Transport getTransport() {
        return this.transport;
    }

    @Override
    public String getName() {
        return SinapayConstants.SIGN_PROVIDER_NAME;
    }


}
